把普通视频变成实况照片 PRO
把手上已有的普通视频——录屏、相册里导出的片段、下载来的短片——变成系统照片库里真正的实况照片。
整个流程就这些。下面的内容只在你想控制结果时才需要。
为什么随便一张图配一段视频不行
实况照片不是「一张 JPEG 挨着一个 MP4」。照片库只有在下面三件事对上时才认这一对文件:
- 静态图的 Apple Maker Note 里带着实况照片的 asset identifier;
- 视频的 content identifier 与之完全相同;
- 视频里有一条 still-image-time 元数据轨,标记哪一刻是封面帧。
用 AVAsset 抽一帧、再用 UIImage.toJPEGData() 重新编码,然后把两个文件交给 Photos.saveLivePhoto——结果是报 PHPhotosErrorDomain 3302,因为解码后的位图没有 identifier,普通 .mp4 也没有那条元数据轨。
LivePhoto.createFromVideo 会替你把这三件事全部写好,你不需要碰任何元数据。
选择封面帧
静态图就是这张实况照片在相册里「静止时」显示的那一帧。默认取源视频的中点,想换一刻就传 stillTime(单位秒,相对源视频起点):
保留下来的视频片段会以这一刻为中心。超出视频范围的值会被钳定而不是报错,所以传一个大致的估计值也是安全的。
时长
实况照片通常都很短。maxDuration 默认 10 秒——更长的源视频会围绕 stillTime 裁出一个窗口:
注意传入的 stillTime 相对源视频,返回的 stillTime 相对裁剪后的输出视频。
除了源视频本身的长度之外没有其它上限。想要更长、或者整段都要:
转换阶段的时长成本几乎可以忽略——视频轨是直接复制而不是重新编码,60 秒的片段主要就是文件 I/O。但有两点要留意:再长的实况照片在照片库里也只是一个条目,却要占掉整段视频的空间;而超长实况照片的播放行为由 iOS 决定。真正会因为时长变慢的只有少见的重编码回落路径——在意的话看返回值里的 reencoded。
输出文件
不指定路径时两个文件都写在临时目录,返回值里的路径永远是实际写入的位置:
配对视频必须是 .mov:照片库不接受 .mp4 作为配对视频,所以非 .mov 的输出路径会被提前拒绝。静态图可以是 "jpeg"(默认)或 "heic"。
不想把原声带进实况照片,传 includeAudio: false。
返回值
reencoded 对绝大多数视频都是 false:原视频轨会被原样复制过去,又快又无损。只有当源编码放不进 QuickTime 容器、不得不重新编码时才会变成 true——更慢,且有轻微质量损失。
方向
旋转已经处理好了。竖拍视频在配对视频里保持原方向,静态图也会以旋转后的方向写出,封面和播放画面不会对不上。
错误
以下情况 Promise 会带着可读的错误信息被拒绝:
- 源文件不存在,或不是媒体文件;
- 源文件没有视频轨(例如纯音频文件);
- 视频输出路径不是
.mov,或所在目录不存在; - 视频没有可播放的时长。
不会留下半成品——视频写失败时,静态图也会被一并清理。
存入照片库
Photos.saveLivePhoto 会把两个文件复制进照片库。只有在你确定不再需要这两个文件时才传 shouldMoveFile: true,因为它是移走而不是复制:
查看 identifier
如果你想确认(或排查配对问题),identifier 可以通过 ImageIO 读出来:
你也可以自己指定 identifier(例如想用同一身份重建一对资源),但它必须是 UUID 字符串。静态图里存放它的字段是定长的,更短的值会被填充,结果就与视频对不上;非 UUID 的值会被直接拒绝,而不是悄悄产出一对照片库不认的文件。
相关内容
- Live Photo 拍摄——如果素材来自相机,
AVCapturePhotoOutput可以直接拍出实况照片,见 AVCaptureSession 下的 Live Photo 一页,这种情况不需要createFromVideo。 - LivePhotoView——在界面里展示结果。
- ImageIO——读写图片元数据,包括
makerApple。
